package com.dc.project.workflow.service.impl;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.dc.common.constant.PrefixConstants;
import com.dc.common.constant.TableConstants;
import com.dc.common.constant.WorkflowConstants;
import com.dc.common.core.ActFowDto;
import com.dc.common.core.LoginUser;
import com.dc.common.exception.ServiceException;
import com.dc.common.utils.*;
import com.dc.common.vo.PageResultVo;
import com.dc.project.gen.utils.GenerateCodeUtil;
import com.dc.project.workflow.config.command.JumpAnyWhereCmd;
import com.dc.project.workflow.config.ext.CustomProcessDiagramGenerator;
import com.dc.project.workflow.dao.ActFlowModelDao;
import com.dc.project.workflow.dto.FlowData;
import com.dc.project.workflow.entity.WfCategory;
import com.dc.project.workflow.service.ActFlowService;
import com.dc.project.workflow.service.WfDocProcessService;
import com.dc.project.workflow.service.WfModelCategoryService;
import com.dc.project.workflow.utils.ActivitiUtil;
import com.dc.project.workflow.utils.ProcessUtil;
import com.dc.project.workflow.vo.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Charsets;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhuangcy
 * @date 2021/6/5
 * @description 模型库服务接口实现类
 */
@Slf4j
@Service("actFlowService")
public class ActFlowServiceImpl implements ActFlowService, ModelDataJsonConstants {
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private HistoryService historyService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private ProcessEngineConfiguration processEngineConfiguration;
    @Resource
    private ActFlowModelDao actFlowModelDao;
    @Resource
    private ProcessEngineServiceImpl processEngineService;
    @Resource
    private WfModelCategoryService wfModelCategoryService;

    /**
     * 查询模型列表
     *
     * @param vo
     * @return
     */
    @Override
    public PageResultVo<ProcessModelVo> queryModelPage(ProcessQueryVo vo) {
        IPage<ProcessModelVo> page = PageUtil.convert2PageParam(vo);
        IPage<ProcessModelVo> processModels = actFlowModelDao.listProcessModels(page, vo);
        return PageUtil.convert2PageResult(processModels);
    }

    /**
     * 创建流程模型
     *
     * @param modelVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createModel(ModelVo modelVo) {
        // 校验是否存在
        if (StringUtil.isNotEmpty(modelVo.getModelKey())) {
            int count = actFlowModelDao.existModelKey(modelVo.getModelKey());
            if (count > 0) {
                throw new ServiceException("该Key值已存在");
            }
        } else {
            modelVo.setModelKey(GenerateCodeUtil.getDefaultCode(PrefixConstants.MODEL_CODE_PREFIX, TableConstants.WF_MODEL));
        }
        ObjectNode editorNode = ObjectMapperUtil.MAPPER.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = ObjectMapperUtil.MAPPER.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);
        Model model = repositoryService.newModel();

        ObjectNode modelObjectNode = ObjectMapperUtil.MAPPER.createObjectNode();
        modelObjectNode.put(MODEL_NAME, modelVo.getModelName());
        modelObjectNode.put(MODEL_REVISION, 1);
        modelObjectNode.put(MODEL_DESCRIPTION, modelVo.getDescription());
        model.setMetaInfo(modelObjectNode.toString());
        model.setName(modelVo.getModelName());
        model.setKey(modelVo.getModelKey());
        // 保存模型
        repositoryService.saveModel(model);
        repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8));
        log.debug("模型生成完成ID={}", model.getId());
        return model.getId();
    }

    /**
     * 发布模型实例
     *
     * @param modelId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Model publishModel(String modelId) {
        Model model = getModel(modelId);
        try {
            String oldDeploymentId = model.getDeploymentId();
            // 获取bpmn资源
            byte[] editorSource = repositoryService.getModelEditorSource(modelId);
            // 获取image资源
            byte[] sourceExtra = repositoryService.getModelEditorSourceExtra(modelId);
            if (editorSource == null || sourceExtra == null) {
                log.debug("模型ID:{}的模型数据为空，请先设计流程并成功保存，再进行发布", modelId);
                throw new ServiceException("发布的模型数据为空，请先设计流程并成功保存，再进行发布");
            }
            ByteArrayInputStream inputStream = new ByteArrayInputStream(sourceExtra);
            JsonNode modelNode = ObjectMapperUtil.MAPPER.readTree(editorSource);
            BpmnModel bm = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if (bm == null || bm.getLocationMap().isEmpty()) {
                throw new ServiceException("发布的模型数据为空，请先设计流程并成功保存，再进行发布");
            }
            Deployment deployment = repositoryService.createDeployment()
                    .key(model.getKey())
                    .name(model.getName())
                    //这里后缀名只能是 bpmn20.xml或bpmn
                    .addBpmnModel(model.getKey() + WorkflowConstants.BPMN_SUFFIX, bm)
                    .addInputStream(model.getKey() + WorkflowConstants.BPMN_PNG, inputStream)
                    .deploy();
            inputStream.close();
            model.setDeploymentId(deployment.getId());
            // 发布版本+1
            int version = model.getVersion() + 1;
            model.setVersion(version);
            repositoryService.saveModel(model);
            if (StringUtil.isNoneBlank(model.getName())) {
                actFlowModelDao.updateProcessDefinitionName(model.getName(), deployment.getId());
                log.debug("流程模型【{}】没有配置流程名称，默认使用流程模型名称作为流程名称", model.getName());
            }
            log.debug("模型ID:{}发布成功,发布ID:{},发布旧ID:{}", modelId, deployment.getId(), oldDeploymentId);
            return model;
        } catch (ServiceException e) {
            throw new ServiceException(e.getMessage(), e);
        } catch (Exception e) {
            throw new ServiceException("发布模型服务异常", e);
        }
    }


    /**
     * 删除流程实例
     *
     * @param modelId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteModel(String modelId) {
        Model model = getModel(modelId);
        // 这里将会删除与该流程模型相关的数据，慎重！！！
        try {
            List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKey(model.getKey()).list();
            for (ProcessInstance pi : list) {
                if (null != pi) {
                    // 删除流程实例
                    runtimeService.deleteProcessInstance(pi.getId(), "");
                    // 删除历史记录
                    historyService.deleteHistoricProcessInstance(pi.getId());
                }
            }
            // 删除流程发布信息
            List<Deployment> deploymentList = repositoryService.createDeploymentQuery().deploymentKey(model.getKey()).list();
            for (Deployment deployment : deploymentList) {
                repositoryService.deleteDeployment(deployment.getId(), true);
            }
            // 最后删除流程模型
            repositoryService.deleteModel(modelId);
            // 删除模型ID删除关联信息
            wfModelCategoryService.deleteByModelId(modelId);
            return true;
        } catch (Exception e) {
            throw new ServiceException("删除流程实例服务异常", e);
        }
    }


    /**
     * 创建审批流程
     *
     * @param businessKey 业务key
     * @param docType     单据类型
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createDocProcess(String businessKey, String docType) {
        String modelId = wfModelCategoryService.getCategoryModelIdByType(docType);
        if (StringUtil.isEmpty(modelId)) {
            throw new ServiceException("未找到单据关联审批流程模型，请前往关联");
        }
        Model model = repositoryService.getModel(modelId);
        if (null == model) {
            throw new ServiceException("未找到单据关联审批流程模型，请前往关联");
        }
        if (StringUtil.isEmpty(model.getDeploymentId())) {
            throw new ServiceException("关联审批流程模型未发布，请前往发布");
        }
        String businessId = getDocBusinessKey(businessKey, docType);
        String userCode = ShiroUtil.getUserInfo().getUserCode();
        return processEngineService.start(model.getDeploymentId(), businessId, userCode);
    }

    private String getDocBusinessKey(String businessKey, String docType) {
        return businessKey + WorkflowConstants.SEPARATOR + docType;
    }


    /**
     * 提交任务
     *
     * @param docCode
     * @param typeName
     * @param dataMap
     */
    @Override
    public void submitDocTask(String docCode, String typeName, Map<String, Object> dataMap) {
        String businessKey = getDocBusinessKey(docCode, typeName);
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
        Map<String, Object> currentUserTaskVariables = processEngineService.getCurrentUserTaskVariables(task.getId());
        FlowData flowData = (FlowData) currentUserTaskVariables.get(ProcessUtil.FLOW_DATA_KEY);
        flowData.setComment(ProcessUtil.DEFAULT_AGREE_SUBMIT);
//        Map<String, Object> docDataMap = (Map<String, Object>) currentUserTaskVariables.get(ProcessUtil.FLOW_VARIABLES_KEY);
//        docDataMap.putAll(dataMap);
        runtimeService.setVariables(task.getExecutionId(), dataMap);
        processEngineService.submitTask(currentUserTaskVariables);
    }

    /**
     * 撤回单据
     *
     * @param docCode
     * @param typeName
     * @param dataMap
     */
    @Override
    public void revokeDocTask(String docCode, String typeName, Map<String, Object> dataMap) {
        String businessKey = getDocBusinessKey(docCode, typeName);
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
        processEngineService.backToFirstNode(task.getId(), ProcessUtil.DEFAULT_AGREE_BACK);
    }

    /**
     * 执行任务
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void completeTask(ActFowDto dto) {
        String businessKey = dto.getBusinessKey();
        String taskId = dto.getTaskId();
        String category = dto.getCategory();
        WfDocProcessService docProcessService = getWfDocProcessService(category);
        String docStatus = docProcessService.getDocStatus(businessKey);
        if (WorkflowConstants.DOC_SUBMIT_STATE.equals(docStatus)
                || WorkflowConstants.DOC_AUDITING_STATE.equals(docStatus)) {
            Map<String, Object> currentUserTaskVariables = processEngineService.getCurrentUserTaskVariables(taskId);
            FlowData flowData = (FlowData) currentUserTaskVariables.get(ProcessUtil.FLOW_DATA_KEY);
            // 前端传来的审批意见
            flowData.setComment(dto.getComment());
            flowData.setTaskId(dto.getTaskId());
            processEngineService.submitTask(currentUserTaskVariables);
            // 这里得去修改单据的审核状态
//            int nextNodeNum = flowData.getNextNodeNum();
            long nextNodeNum = taskService.createTaskQuery()
                    .processInstanceBusinessKey(getDocBusinessKey(businessKey, category))
                    .count();
            if (nextNodeNum == 0) {
                docProcessService.updateDocStatus(businessKey, WorkflowConstants.DOC_APPROVED_STATE);
            } else {
                docProcessService.updateDocStatus(businessKey, WorkflowConstants.DOC_AUDITING_STATE);
            }
        } else {
            throw new ServiceException(StringUtil.format("该{}单据状态不可审批", businessKey));
        }

    }

    /**
     * 驳回审批到起点操作
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rejectTaskToFirst(ActFowDto dto) {
        processEngineService.backToFirstNode(dto.getTaskId());
        // 修改单据的审核状态
        WfDocProcessService processService = getWfDocProcessService(dto.getCategory());
        processService.updateDocStatus(dto.getBusinessKey(), WorkflowConstants.DOC_APPROVED_NOT_STATE);
    }

    /**
     * 驳回审批到上一节点操作
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rejectTaskToParent(ActFowDto dto) {
        boolean isFirstNode = processEngineService.backToPreNode(dto.getTaskId(), dto.getComment());
        // 判断上一节点是否为提交节点
        // 修改单据的审核状态
        WfDocProcessService processService = getWfDocProcessService(dto.getCategory());
        if (isFirstNode) {
            processService.updateDocStatus(dto.getBusinessKey(), WorkflowConstants.DOC_APPROVED_NOT_STATE);
        } else {
            processService.updateDocStatus(dto.getBusinessKey(), WorkflowConstants.DOC_AUDITING_STATE);
        }
    }

    /**
     * 驳回审批到任意节点操作
     *
     * @param dto
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rejectTaskToAny(ActFowDto dto) {
        boolean isFirstNode = processEngineService.backToAnyNode(dto.getTaskId(), dto.getTargetNodeId(), dto.getComment());
        // 判断上一节点是否为提交节点
        // 修改单据的审核状态
        WfDocProcessService processService = getWfDocProcessService(dto.getCategory());
        if (isFirstNode) {
            processService.updateDocStatus(dto.getBusinessKey(), WorkflowConstants.DOC_APPROVED_NOT_STATE);
        } else {
            processService.updateDocStatus(dto.getBusinessKey(), WorkflowConstants.DOC_AUDITING_STATE);
        }

    }

    /**
     * 获取单据处理接口
     *
     * @param category
     * @return
     */
    private WfDocProcessService getWfDocProcessService(String category) {
        WfCategory wfCategory = wfModelCategoryService.getClassNameByCategory(category);
        if (null == wfCategory) {
            throw new ServiceException("审批时处理单据信息失败");
        }
        WfDocProcessService docProcessService;
        if (StringUtil.isNotEmpty(wfCategory.getBeanName())) {
            docProcessService = SpringUtil.getBean(wfCategory.getBeanName());
        } else {
            docProcessService = SpringUtil.getBeanByClass(wfCategory.getBeanClass());
        }
        return docProcessService;
    }

    /**
     * 获取流程模型
     *
     * @param modelId
     * @return
     */
    private Model getModel(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if (null == model) {
            throw new ServiceException("未找到流程模型");
        }
        return model;
    }

    /**
     * 获取编辑器Json
     *
     * @param modelId
     * @return
     */
    @Override
    public ObjectNode getEditorJson(String modelId) {
        ObjectNode modelNode = null;
        Model model = getModel(modelId);
        try {
            if (StringUtils.isNotEmpty(model.getMetaInfo())) {
                modelNode = (ObjectNode) ObjectMapperUtil.MAPPER.readTree(model.getMetaInfo());
            } else {
                modelNode = ObjectMapperUtil.MAPPER.createObjectNode();
                modelNode.put(MODEL_NAME, model.getName());
            }
            modelNode.put(MODEL_ID, model.getId());
            ObjectNode editorJsonNode = (ObjectNode) ObjectMapperUtil.MAPPER.readTree(new String(repositoryService.getModelEditorSource(model.getId()), Charsets.UTF_8));
            modelNode.put("model", editorJsonNode);
        } catch (Exception e) {
            log.error("Error creating model JSON", e);
            throw new ServiceException("创建模型 JSON 时出错", e);
        }
        return modelNode;
    }

    /**
     * 保存模型资源
     *
     * @param modelId
     * @param values
     */
    @Override
    public void saveModel(String modelId, MultiValueMap<String, String> values) {
        Model model = getModel(modelId);
        try {
            ObjectNode modelJson = (ObjectNode) ObjectMapperUtil.MAPPER.readTree(model.getMetaInfo());
            modelJson.put(MODEL_NAME, values.getFirst("name"));
            modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
            model.setMetaInfo(modelJson.toString());
            model.setName(values.getFirst("name"));
            repositoryService.saveModel(model);
            ObjectNode jsonXml = (ObjectNode) ObjectMapperUtil.MAPPER.readTree(values.getFirst("json_xml"));
            ObjectNode properties = (ObjectNode) jsonXml.get("properties");
            // 设置模型ID，不允许修改，保证唯一
            properties.put("process_id", model.getKey());
            jsonXml.put("properties", properties);
            repositoryService.addModelEditorSource(model.getId(), jsonXml.toString().getBytes(StandardCharsets.UTF_8));
            InputStream svgStream = new ByteArrayInputStream(values.getFirst("svg_xml").getBytes(StandardCharsets.UTF_8));
            TranscoderInput input = new TranscoderInput(svgStream);
            PNGTranscoder transcoder = new PNGTranscoder();
            // 设置输出流
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            TranscoderOutput output = new TranscoderOutput(outStream);
            // 进行改造
            transcoder.transcode(input, output);
            final byte[] result = outStream.toByteArray();
            repositoryService.addModelEditorSourceExtra(model.getId(), result);
            outStream.close();
        } catch (Exception e) {
            log.error("保存模型时出错", e);
            throw new ServiceException("保存模型时出错", e);
        }
    }

    /**
     * 获取模板集
     *
     * @param modelId
     * @return
     */
    @Override
    public String getStencilSet(String modelId) {
        Model model = getModel(modelId);
        try {
            InputStream is = this.getClass().getClassLoader().getResourceAsStream("static/stencilset.json");
            String json = IOUtils.toString(is, Charsets.UTF_8);
            // 设置模型ID
            ObjectNode objectNode = (ObjectNode) ObjectMapperUtil.MAPPER.readTree(json);
            ObjectNode node = (ObjectNode) objectNode.get("propertyPackages").get(0).get("properties").get(0);
            node.put("value", model.getKey());
            json = objectNode.toString();
            return json;
        } catch (Exception e) {
            throw new ServiceException("加载模板集时出错", e);
        }
    }

    /**
     * 流程定义挂起或激活
     * 流程定义挂起时，底下的流程实例也会挂起
     * 挂起时不允许在启动新的流程实例
     *
     * @param modelId 流程模型id
     * @return
     */
    @Override
    public boolean suspendOrActivateProcessDefinition(String modelId) {
        Model model = getModel(modelId);
        if (StringUtil.isEmpty(model.getDeploymentId())) {
            throw new ServiceException("未找到流程发布信息");
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(model.getDeploymentId()).singleResult();
        if (null == processDefinition) {
            throw new ServiceException("未找到流程定义");
        }
        // 流程定义ID
        String processDefinitionId = processDefinition.getId();
        // 暂停状态
        boolean suspended = processDefinition.isSuspended();
        if (suspended) {
            //激活  流程定义id、是否激活、激活时间
            repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
            log.debug("激活成功{}", processDefinitionId);
        } else {
            // 挂起 流程定义id、是否暂停、暂停时间
            repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
            log.debug("挂起成功{}", processDefinitionId);
        }
        return suspended;
    }

    /**
     * 根据流程实例Id,获取实时流程图片
     *
     * @param processInstanceId
     * @param processDefinitionId
     * @return
     */
    public byte[] getFlowImgByProcInstId(String processInstanceId, String processDefinitionId) {
        if (StringUtils.isEmpty(processInstanceId)) {
            log.error("[错误]-传入的参数procInstId为空！");
            return new byte[0];
        }
        InputStream imageStream = null;
        try {
            // 通过流程实例ID获取流程中已经执行的节点，按照执行先后顺序排序
            // 将已经执行的节点ID放入高亮显示节点集合
            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().asc().list();
            List<String> highLightedActivitiIdList = historicActivityInstanceList.stream().map(HistoricActivityInstance::getActivityId).collect(Collectors.toList());

            // 通过流程实例ID获取流程中正在执行的节点
            List<Execution> runningActivityInstanceList = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
            List<String> runningActivitiIdList = runningActivityInstanceList.stream().filter(execution -> StringUtils.isNotEmpty(execution.getActivityId())).map(Execution::getActivityId).collect(Collectors.toList());

            // 通过流程实例ID获取已经完成的历史流程实例
            // List<HistoricProcessInstance> historicFinishedProcessInstanceList = getHistoricFinishedProcInst(processInstanceId);

            // 定义流程画布生成器
            CustomProcessDiagramGenerator processDiagramGenerator = null;
            // 如果还没完成，流程图高亮颜色为红色，如果已经完成为绿色
            // if (!CollectionUtils.isEmpty(historicFinishedProcessInstanceList)) {
            // // 如果不为空，说明已经完成
            // processDiagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
            // } else {
            processDiagramGenerator = new CustomProcessDiagramGenerator();
            // }

            // 获取流程定义Model对象
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

            // 获取已流经的流程线，需要高亮显示高亮流程已发生流转的线id集合
            List<String> highLightedFlowIds = ActivitiUtil.getHighLightedFlows(bpmnModel, historicActivityInstanceList);

            // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
            imageStream = processDiagramGenerator.generateDiagramCustom(bpmnModel, WorkflowConstants.BPMN_TYPE_PNG,
                    highLightedActivitiIdList, runningActivitiIdList, highLightedFlowIds,
                    processEngineConfiguration.getActivityFontName(),
                    processEngineConfiguration.getLabelFontName(),
                    processEngineConfiguration.getAnnotationFontName(),
                    null, 1.0);
            // 将InputStream数据流转换为byte[]
            byte[] buffer = new byte[imageStream.available()];
            imageStream.read(buffer);
            return buffer;
        } catch (Exception e) {
            log.error("通过流程实例ID[{}]获取流程图时出现异常！", e.getMessage(), e);
        } finally {
            if (imageStream != null) {
                try {
                    imageStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return new byte[0];

    }

    /**
     * 获取流程实例任务图像
     *
     * @param key
     * @param category
     * @return
     */
    @Override
    public byte[] getTaskImage(String key, String category) {
        String businessKey = getDocBusinessKey(key, category);
        // 获取流程实例
        HistoricProcessInstanceEntityImpl processInstance = (HistoricProcessInstanceEntityImpl) ProcessUtil.getProcessInstanceByBusinessKey(businessKey);
        return getFlowImgByProcInstId(processInstance.getProcessInstanceId(), processInstance.getProcessDefinitionId());
    }

    /**
     * 获取流程实例审批历史记录信息
     *
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> getUserTaskHistory(ActFowDto dto) {
        String businessKey = getDocBusinessKey(dto.getBusinessKey(), dto.getCategory());
        HistoricProcessInstanceEntityImpl processInstanceEntity = (HistoricProcessInstanceEntityImpl) ProcessUtil.getProcessInstanceByBusinessKey(businessKey);
        List<HistoricActivityInstance> historicActivityInstanceAsc = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceEntity.getProcessInstanceId())
                .orderByHistoricActivityInstanceStartTime().asc()
                .orderByHistoricActivityInstanceId().asc()
                .list();
        List<CommentVo> records = new ArrayList<>();
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceAsc) {
            // 获取所需参数
            String taskId = historicActivityInstance.getTaskId();
            Date endTime = historicActivityInstance.getEndTime();
            CommentVo commentVo = new CommentVo();
            commentVo.setTaskId(taskId);
            String activityType = historicActivityInstance.getActivityType();
            if ("startEvent".equals(activityType)) {
                commentVo.setTaskName("开始");
            } else if ("endEvent".equals(activityType)) {
                commentVo.setTaskName("结束");
            } else if (StringUtil.isNotEmpty(activityType) && activityType.contains("Gateway")) {
                continue;
            } else {
                commentVo.setTaskName(historicActivityInstance.getActivityName());
            }
            commentVo.setType(activityType);
            commentVo.setStartTime(historicActivityInstance.getStartTime());
            commentVo.setEndTime(endTime);
            commentVo.setAssignee(historicActivityInstance.getAssignee());
            commentVo.setDuration(historicActivityInstance.getDurationInMillis());
            List<Comment> taskComments = taskService.getTaskComments(taskId, JumpAnyWhereCmd.TYPE);

            if (endTime != null) {
                if (1 == taskComments.size()) {
                    commentVo.setFullMessage(taskComments.get(0).getFullMessage());
                } else {
                    for (Comment comment : taskComments) {
                        // TODO: 2021/7/3  这里的校验得改改，有个时间差值，不一定完全相等
                        if (DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD_HH_MM_SS, endTime)
                                .equals(DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD_HH_MM_SS, comment.getTime()))) {
                            commentVo.setFullMessage(comment.getFullMessage());
                            break;
                        }
                    }
                }
            }
            records.add(commentVo);
        }
        Map<String, Object> result = new HashMap<>(2);
        result.put("records", records);
        result.put("title", dto.getBusinessKey() + processInstanceEntity.getName());
        return result;
    }

    /**
     * 获取用户待办任务
     *
     * @param dto
     * @return
     */
    @Override
    public PageResultVo<MyTodoVo> getUserTodoTask(ActFowDto dto) {
        LoginUser userInfo = ShiroUtil.getUserInfo();
        if (StringUtil.isEmpty(dto.getAssignee())) {
            dto.setAssignee(userInfo.getUserCode());
        }
//        TaskQuery taskQuery = taskService.createTaskQuery()
//                .taskAssignee(dto.getAssignee())
//                .orderByTaskCreateTime().desc();
//        List<Task> tasks = taskQuery.listPage((dto.getPageNum() - 1) * dto.getPageSize(), dto.getPageSize());
//        long count = taskQuery.count();

        // 代理人
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(dto.getAssignee()).orderByTaskCreateTime().desc().list();

        // TODO 过滤部门的组
        // 查询候选组任务
        List<String> candidateGroups = userInfo.getPosts();
        List<Task> groupList = taskService.createTaskQuery().taskCandidateGroupIn(candidateGroups).orderByTaskCreateTime().desc().list();
        if (!groupList.isEmpty()) {
            tasks.addAll(groupList);
        }
        // 查询候选人任务
        List<Task> userList = taskService.createTaskQuery().taskCandidateUser(dto.getAssignee()).orderByTaskCreateTime().desc().list();
        if (!userList.isEmpty()) {
            tasks.addAll(userList);
        }

        List<MyTodoVo> todoVos = new ArrayList<>();
        for (Task task : tasks) {
            MyTodoVo todoVo = new MyTodoVo();
            BeanUtil.copyBeanProp(todoVo, task);
            HistoricProcessInstance historicProcessInstance = ProcessUtil.getHistoricProcessInstance(task.getProcessInstanceId());
            String[] businessKey = getBusinessKeySplit(historicProcessInstance);
            todoVo.setProcessName(historicProcessInstance.getName());
            todoVo.setStartUser(historicProcessInstance.getStartUserId());
            todoVo.setBusinessKey(businessKey[0]);
            todoVo.setCategory(businessKey[1]);
//            todoVo.setBusinessUser(StringUtil.isEmpty(businessKey[2]) ? null : Integer.parseInt(businessKey[2]));
            todoVos.add(todoVo);
        }
        // 去重
        List<MyTodoVo> todoVoList = todoVos.stream().distinct().collect(Collectors.toList());
        PageResultVo<MyTodoVo> resultVo = new PageResultVo<>();
//        resultVo.setTotal(count);
        resultVo.setList(todoVoList);
        return resultVo;
    }

    private String[] getBusinessKeySplit(HistoricProcessInstance historicProcessInstance) {
        return historicProcessInstance.getBusinessKey().split(WorkflowConstants.SEPARATOR);
    }

    /**
     * 获取用户已办任务
     *
     * @param dto
     * @return
     */
    @Override
    public PageResultVo<MyDoneVo> getUserDoneTask(ActFowDto dto) {
        if (StringUtil.isEmpty(dto.getAssignee())) {
            dto.setAssignee(ShiroUtil.getUserInfo().getUserCode());
        }

        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(dto.getAssignee())
                .finished()
                .orderByHistoricTaskInstanceEndTime().desc();
        List<HistoricTaskInstance> historicTaskInstances = taskInstanceQuery.listPage((dto.getPageNum() - 1) * dto.getPageSize(), dto.getPageSize());
        long count = taskInstanceQuery.count();

        List<MyDoneVo> doneVos = new ArrayList<>();
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
            MyDoneVo myDoneVo = new MyDoneVo();
            BeanUtil.copyBeanProp(myDoneVo, historicTaskInstance);
            HistoricProcessInstance historicProcessInstance = ProcessUtil.getHistoricProcessInstance(historicTaskInstance.getProcessInstanceId());
            String[] businessKey = getBusinessKeySplit(historicProcessInstance);
            myDoneVo.setProcessName(historicProcessInstance.getName());
            myDoneVo.setStartUser(historicProcessInstance.getStartUserId());
            myDoneVo.setBusinessKey(businessKey[0]);
            myDoneVo.setCategory(businessKey[1]);
            // 审批操作
            String deleteReason = historicTaskInstance.getDeleteReason();
            if (null != deleteReason) {
                if (deleteReason.contains(ProcessUtil.DEFAULT_AGREE_REJECT)) {
                    myDoneVo.setApproveAction(ProcessUtil.DEFAULT_AGREE_REJECT);
                } else if (deleteReason.contains(ProcessUtil.DEFAULT_AGREE_BACK)) {
                    myDoneVo.setApproveAction(ProcessUtil.DEFAULT_AGREE_BACK);
                } else {
                    myDoneVo.setApproveAction(ProcessUtil.DEFAULT_AGREE_COMMENT);
                }
            } else {
                myDoneVo.setApproveAction(ProcessUtil.DEFAULT_AGREE_COMMENT);
            }
            if (historicProcessInstance.getEndTime() == null) {
                myDoneVo.setProcessStatus(ProcessUtil.FLOW_STATUS_RUNNING);
            } else {
                myDoneVo.setProcessStatus(ProcessUtil.FLOW_STATUS_END);
            }
            doneVos.add(myDoneVo);
        }
        PageResultVo<MyDoneVo> resultVo = new PageResultVo<>();
        resultVo.setTotal(count);
        resultVo.setList(doneVos);
        return resultVo;
    }

    /**
     * 获取审批流程任务信息
     *
     * @param taskId
     * @return
     */
    @Override
    public FlowData getTaskFlowData(String taskId) {
        return ProcessUtil.getFlowData(taskId);
    }

    /**
     * 删除流程信息
     *
     * @param docCode
     * @param docTypeName
     */
    @Override
    public void deleteDocProcess(String docCode, String docTypeName) {
        String businessKey = getDocBusinessKey(docCode, docTypeName);
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .list();
        for (ProcessInstance processInstance : list) {
            if (processInstance != null) {
                // 删除流程实例
                runtimeService.deleteProcessInstance(processInstance.getId(), "");
                // 删除历史记录
                historyService.deleteHistoricProcessInstance(processInstance.getId());
            }
        }

    }
}
